﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using LinqKit;
namespace DynamicQueryLib
{
    class LambdaExpressionHelper<EntityType>
    {
        public static System.Linq.Expressions.Expression<Func<EntityType, bool>> CreatePredicate<EntityType>(List<Filter> filterList)
        {
            var predicate = PredicateBuilder.True<EntityType>();
            foreach (var f in filterList)
            {
                if (f != null)
                {

                    if (f.Connector == FilterConnectorType.AND)
                    {

                        //predicate = predicate.And(LambdaExpressionHelper<Archive>.ExpressionBuilder<int>  (f));//.ExpressionBuilder<int>(f));
                        //predicate = predicate.And<Archive>(f);
                        predicate = predicate.And(LambdaExpressionHelper<EntityType>.ExpressionBuilder(f));
                    }
                    else
                    {
                        predicate = predicate.Or(LambdaExpressionHelper<EntityType>.ExpressionBuilder(f));
                    }
                }
            }
            return predicate;
        }
        //private static Expression<Func<EntityType, bool>> ExpressionBuilder(string propertyName, string constantValue)
        //{
        //    ParameterExpression param = Expression.Parameter(typeof(EntityType), "k");
        //    MemberExpression property = Expression.Property(param, typeof(EntityType).GetProperty(propertyName));
        //    ConstantExpression constValue = Expression.Constant(constantValue, typeof(string));
        //    MethodCallExpression call = Expression.Call(property, typeof(string).GetMethod("Contains"),
        //        new ConstantExpression[] { constValue });
        //    //Expression ET = Expression.Equal(property, constValue);
        //    Expression<Func<EntityType, bool>> exp = Expression.Lambda<Func<EntityType, bool>>(call,
        //        new ParameterExpression[] { param });
        //    return exp.Expand();
        //}
        private static Expression<Func<EntityType, bool>> ExpressionBuilder(
            string propertyName, Type propertyType, string constantValue, RelationalOperators op)
        {
            ParameterExpression param = Expression.Parameter(typeof(EntityType), "k");
            MemberExpression property = Expression.Property(param, typeof(EntityType).GetProperty(propertyName));
            //ConstantExpression constValue = Expression.Constant(constantValue, propertyType);
            ConstantExpression constValue;
            MethodCallExpression call;
            if (propertyType.Equals(typeof(int)))
            {
                constValue = Expression.Constant(Convert.ToInt32(constantValue), propertyType);
                call = Expression.Call(Expression.Convert(property, typeof(Int32)),//Avoid the Boolean Equals(Int32)' declared on type 'System.Int32' cannot be called with instance of type 'System.Nullable, Expression.Call
                    propertyType.GetMethod(op.ToString(), new Type[] { typeof(Int32) }),//"Equals"),
                new ConstantExpression[] { constValue });
            }
            else
            {
                constValue = Expression.Constant(constantValue, propertyType);
                call = Expression.Call(property, propertyType.GetMethod(op.ToString()),//"Equals"),
                new ConstantExpression[] { constValue });
            }

            //Expression ET = Expression.Equal(property, constValue);
            Expression<Func<EntityType, bool>> exp = Expression.Lambda<Func<EntityType, bool>>(call,
                new ParameterExpression[] { param });
            return exp.Expand();
        }
        public static Expression<Func<EntityType, bool>> ExpressionBuilder(Filter filter)
        {
            var predicate = PredicateBuilder.False<EntityType>();
            //foreach (string v in filter.Values)
            foreach (string v in filter.Values)
            {
                predicate = predicate.Or(ExpressionBuilder(filter.PropertyName, filter.PropertyType, v, filter.Operator));
            }

            return predicate.Expand();
        }

        public static Expression<Func<EntityType, string>> SelectExpression(string propertyName)
        {
            ParameterExpression param = Expression.Parameter(typeof(EntityType), "t");
            MemberExpression property = Expression.Property(param, typeof(EntityType).GetProperty(propertyName));
            Expression<Func<EntityType, string>> exp = Expression.Lambda<Func<EntityType, string>>(property, new ParameterExpression[] { param });
            return exp.Expand();
        }
    }

    public enum RelationalOperators { Equals, Contains }
}
